// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <algorithm>
#include <vector>

#include "gmock/gmock.h"
#include "src/reader/spirv/parser_impl_test_helper.h"

namespace tint {
namespace reader {
namespace spirv {
namespace {

using ::testing::Eq;

TEST_F(SpvParserTest, Usage_Trivial_Properties) {
  Usage u;
  EXPECT_TRUE(u.IsValid());
  EXPECT_FALSE(u.IsComplete());
  EXPECT_FALSE(u.IsSampler());
  EXPECT_FALSE(u.IsComparisonSampler());
  EXPECT_FALSE(u.IsTexture());
  EXPECT_FALSE(u.IsSampledTexture());
  EXPECT_FALSE(u.IsMultisampledTexture());
  EXPECT_FALSE(u.IsDepthTexture());
  EXPECT_FALSE(u.IsStorageReadTexture());
  EXPECT_FALSE(u.IsStorageWriteTexture());
}

TEST_F(SpvParserTest, Usage_Trivial_Output) {
  std::ostringstream ss;
  Usage u;
  ss << u;
  EXPECT_THAT(ss.str(), Eq("Usage()"));
}

TEST_F(SpvParserTest, Usage_Equality_OneDifference) {
  const int num_usages = 9;
  std::vector<Usage> usages(num_usages);
  usages[1].AddSampler();
  usages[2].AddComparisonSampler();
  usages[3].AddTexture();
  usages[4].AddSampledTexture();
  usages[5].AddMultisampledTexture();
  usages[6].AddDepthTexture();
  usages[7].AddStorageReadTexture();
  usages[8].AddStorageWriteTexture();
  for (int i = 0; i < num_usages; ++i) {
    for (int j = 0; j < num_usages; ++j) {
      const auto& lhs = usages[i];
      const auto& rhs = usages[j];
      if (i == j) {
        EXPECT_TRUE(lhs == rhs);
      } else {
        EXPECT_FALSE(lhs == rhs);
      }
    }
  }
}

TEST_F(SpvParserTest, Usage_Add) {
  // Mix two nontrivial usages.
  Usage a;
  a.AddStorageReadTexture();

  Usage b;
  b.AddComparisonSampler();

  a.Add(b);

  EXPECT_FALSE(a.IsValid());
  EXPECT_FALSE(a.IsComplete());
  EXPECT_TRUE(a.IsSampler());
  EXPECT_TRUE(a.IsComparisonSampler());
  EXPECT_TRUE(a.IsTexture());
  EXPECT_FALSE(a.IsSampledTexture());
  EXPECT_FALSE(a.IsMultisampledTexture());
  EXPECT_FALSE(a.IsDepthTexture());
  EXPECT_TRUE(a.IsStorageReadTexture());
  EXPECT_FALSE(a.IsStorageWriteTexture());

  std::ostringstream ss;
  ss << a;
  EXPECT_THAT(ss.str(), Eq("Usage(Sampler( comparison )Texture( read ))"));
}

TEST_F(SpvParserTest, Usage_AddSampler) {
  std::ostringstream ss;
  Usage u;
  u.AddSampler();

  EXPECT_TRUE(u.IsValid());
  EXPECT_TRUE(u.IsComplete());
  EXPECT_TRUE(u.IsSampler());
  EXPECT_FALSE(u.IsComparisonSampler());
  EXPECT_FALSE(u.IsTexture());
  EXPECT_FALSE(u.IsSampledTexture());
  EXPECT_FALSE(u.IsMultisampledTexture());
  EXPECT_FALSE(u.IsDepthTexture());
  EXPECT_FALSE(u.IsStorageReadTexture());
  EXPECT_FALSE(u.IsStorageWriteTexture());

  ss << u;
  EXPECT_THAT(ss.str(), Eq("Usage(Sampler( ))"));

  // Check idempotency
  auto copy(u);
  u.AddSampler();
  EXPECT_TRUE(u == copy);
}

TEST_F(SpvParserTest, Usage_AddComparisonSampler) {
  std::ostringstream ss;
  Usage u;
  u.AddComparisonSampler();

  EXPECT_TRUE(u.IsValid());
  EXPECT_TRUE(u.IsComplete());
  EXPECT_TRUE(u.IsSampler());
  EXPECT_TRUE(u.IsComparisonSampler());
  EXPECT_FALSE(u.IsTexture());
  EXPECT_FALSE(u.IsSampledTexture());
  EXPECT_FALSE(u.IsMultisampledTexture());
  EXPECT_FALSE(u.IsDepthTexture());
  EXPECT_FALSE(u.IsStorageReadTexture());
  EXPECT_FALSE(u.IsStorageWriteTexture());

  ss << u;
  EXPECT_THAT(ss.str(), Eq("Usage(Sampler( comparison ))"));

  auto copy(u);
  u.AddComparisonSampler();
  EXPECT_TRUE(u == copy);
}

TEST_F(SpvParserTest, Usage_AddTexture) {
  std::ostringstream ss;
  Usage u;
  u.AddTexture();

  EXPECT_TRUE(u.IsValid());
  EXPECT_FALSE(u.IsComplete());  // Don't know if it's sampled or storage
  EXPECT_FALSE(u.IsSampler());
  EXPECT_FALSE(u.IsComparisonSampler());
  EXPECT_TRUE(u.IsTexture());
  EXPECT_FALSE(u.IsSampledTexture());
  EXPECT_FALSE(u.IsMultisampledTexture());
  EXPECT_FALSE(u.IsDepthTexture());
  EXPECT_FALSE(u.IsStorageReadTexture());
  EXPECT_FALSE(u.IsStorageWriteTexture());

  ss << u;
  EXPECT_THAT(ss.str(), Eq("Usage(Texture( ))"));

  auto copy(u);
  u.AddTexture();
  EXPECT_TRUE(u == copy);
}

TEST_F(SpvParserTest, Usage_AddSampledTexture) {
  std::ostringstream ss;
  Usage u;
  u.AddSampledTexture();

  EXPECT_TRUE(u.IsValid());
  EXPECT_TRUE(u.IsComplete());
  EXPECT_FALSE(u.IsSampler());
  EXPECT_FALSE(u.IsComparisonSampler());
  EXPECT_TRUE(u.IsTexture());
  EXPECT_TRUE(u.IsSampledTexture());
  EXPECT_FALSE(u.IsMultisampledTexture());
  EXPECT_FALSE(u.IsDepthTexture());
  EXPECT_FALSE(u.IsStorageReadTexture());
  EXPECT_FALSE(u.IsStorageWriteTexture());

  ss << u;
  EXPECT_THAT(ss.str(), Eq("Usage(Texture( is_sampled ))"));

  auto copy(u);
  u.AddSampledTexture();
  EXPECT_TRUE(u == copy);
}

TEST_F(SpvParserTest, Usage_AddMultisampledTexture) {
  std::ostringstream ss;
  Usage u;
  u.AddMultisampledTexture();

  EXPECT_TRUE(u.IsValid());
  EXPECT_TRUE(u.IsComplete());
  EXPECT_FALSE(u.IsSampler());
  EXPECT_FALSE(u.IsComparisonSampler());
  EXPECT_TRUE(u.IsTexture());
  EXPECT_TRUE(u.IsSampledTexture());
  EXPECT_TRUE(u.IsMultisampledTexture());
  EXPECT_FALSE(u.IsDepthTexture());
  EXPECT_FALSE(u.IsStorageReadTexture());
  EXPECT_FALSE(u.IsStorageWriteTexture());

  ss << u;
  EXPECT_THAT(ss.str(), Eq("Usage(Texture( is_sampled ms ))"));

  auto copy(u);
  u.AddMultisampledTexture();
  EXPECT_TRUE(u == copy);
}

TEST_F(SpvParserTest, Usage_AddDepthTexture) {
  std::ostringstream ss;
  Usage u;
  u.AddDepthTexture();

  EXPECT_TRUE(u.IsValid());
  EXPECT_TRUE(u.IsComplete());
  EXPECT_FALSE(u.IsSampler());
  EXPECT_FALSE(u.IsComparisonSampler());
  EXPECT_TRUE(u.IsTexture());
  EXPECT_TRUE(u.IsSampledTexture());
  EXPECT_FALSE(u.IsMultisampledTexture());
  EXPECT_TRUE(u.IsDepthTexture());
  EXPECT_FALSE(u.IsStorageReadTexture());
  EXPECT_FALSE(u.IsStorageWriteTexture());

  ss << u;
  EXPECT_THAT(ss.str(), Eq("Usage(Texture( is_sampled depth ))"));

  auto copy(u);
  u.AddDepthTexture();
  EXPECT_TRUE(u == copy);
}

TEST_F(SpvParserTest, Usage_AddStorageReadTexture) {
  std::ostringstream ss;
  Usage u;
  u.AddStorageReadTexture();

  EXPECT_TRUE(u.IsValid());
  EXPECT_TRUE(u.IsComplete());
  EXPECT_FALSE(u.IsSampler());
  EXPECT_FALSE(u.IsComparisonSampler());
  EXPECT_TRUE(u.IsTexture());
  EXPECT_FALSE(u.IsSampledTexture());
  EXPECT_FALSE(u.IsMultisampledTexture());
  EXPECT_FALSE(u.IsDepthTexture());
  EXPECT_TRUE(u.IsStorageReadTexture());
  EXPECT_FALSE(u.IsStorageWriteTexture());

  ss << u;
  EXPECT_THAT(ss.str(), Eq("Usage(Texture( read ))"));

  auto copy(u);
  u.AddStorageReadTexture();
  EXPECT_TRUE(u == copy);
}

TEST_F(SpvParserTest, Usage_AddStorageWriteTexture) {
  std::ostringstream ss;
  Usage u;
  u.AddStorageWriteTexture();

  EXPECT_TRUE(u.IsValid());
  EXPECT_TRUE(u.IsComplete());
  EXPECT_FALSE(u.IsSampler());
  EXPECT_FALSE(u.IsComparisonSampler());
  EXPECT_TRUE(u.IsTexture());
  EXPECT_FALSE(u.IsSampledTexture());
  EXPECT_FALSE(u.IsMultisampledTexture());
  EXPECT_FALSE(u.IsDepthTexture());
  EXPECT_FALSE(u.IsStorageReadTexture());
  EXPECT_TRUE(u.IsStorageWriteTexture());

  ss << u;
  EXPECT_THAT(ss.str(), Eq("Usage(Texture( write ))"));

  auto copy(u);
  u.AddStorageWriteTexture();
  EXPECT_TRUE(u == copy);
}

}  // namespace
}  // namespace spirv
}  // namespace reader
}  // namespace tint
